﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Reflection;
using System.Globalization;

namespace VMExplorer.Dynamic
{
  [TypeConverter (typeof (ExpandableObjectConverter))]
  class FieldsToProperties : ICustomTypeDescriptor
  {
    #region Private fields

    private object _target;

    #endregion
    #region Construction

    public FieldsToProperties (object target) 
		{
			if (target == null) throw new ArgumentNullException ("target");
			_target = target;
    }

    #endregion
    #region Object overrides

    public override string ToString ()
    {
      return string.Format("({0})",_target.GetType().Name);
    }

    #endregion
    #region ICustomTypeDescriptor Members

    object ICustomTypeDescriptor.GetPropertyOwner (PropertyDescriptor pd)
		{
			// Properties belong to the target object
			return _target;
		}

		AttributeCollection ICustomTypeDescriptor.GetAttributes ()
		{
			// Gets the attributes of the target object
			return TypeDescriptor.GetAttributes (this, true);
		}

		string ICustomTypeDescriptor.GetClassName ()
		{
			// Gets the class name of the target object
			return TypeDescriptor.GetClassName (this, true);
		}

		PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties ()
		{
			return ((ICustomTypeDescriptor)this).GetProperties (null);
		}

		private class FilterCache
		{
			public Attribute[] Attributes;
			public PropertyDescriptorCollection FilteredProperties;
			public FilterCache (Attribute [] att, PropertyDescriptorCollection props)
			{
				Attributes = att;
				FilteredProperties = props;
			}

			public bool IsValid (Attribute [] other)
			{
				if (other == null || Attributes == null) return false;

				if (Attributes.Length != other.Length) return false;

				for (int i = 0; i < other.Length; i++)
				{
					if (!Attributes [i].Match (other [i])) return false;
				}

				return true;
			}
		}

		private PropertyDescriptorCollection _propCache;
		private FilterCache _filterCache;

		PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties (Attribute [] attributes)
		{
			bool filtering = (attributes != null && attributes.Length > 0);
			PropertyDescriptorCollection props = _propCache;
			FilterCache cache = _filterCache;

			// Use a cached version if possible
			if (filtering && cache != null && cache.IsValid (attributes))
				return cache.FilteredProperties;

			if (!filtering && props != null)
				return props;

			// Create the property collection and filter
			props = new PropertyDescriptorCollection (null);
			foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties (this, attributes, true))
			{
			  props.Add (prop);
			}

			foreach (FieldInfo field in _target.GetType ().GetFields ())
			{
				FieldPropertyDescriptor fieldDesc = new FieldPropertyDescriptor (field);
				if (!filtering || fieldDesc.Attributes.Contains (attributes))
					props.Add (fieldDesc);
			}

			// Store the computed properties
			if (filtering)
			{
				cache = new FilterCache (attributes, props);
				_filterCache = cache;
			}
			else
			{
				_propCache = props;
			}

			return props;
		}

		string ICustomTypeDescriptor.GetComponentName ()
		{
			return null;
		}

		TypeConverter ICustomTypeDescriptor.GetConverter ()
		{
			return null;
		}

		EventDescriptor ICustomTypeDescriptor.GetDefaultEvent ()
		{
			return null;
		}

		PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty ()
		{
			return null;
		}

		object ICustomTypeDescriptor.GetEditor (Type editorBaseType)
		{
			return null;
		}

		EventDescriptorCollection ICustomTypeDescriptor.GetEvents (Attribute [] attributes)
		{
			return null;
		}

		EventDescriptorCollection ICustomTypeDescriptor.GetEvents ()
		{
			return null;
		}

		#endregion
	}
}
